package com.listeningframework.boot.codegen.generators.coders;

import com.listeningframework.boot.codegen.constants.Templates;
import com.listeningframework.boot.codegen.context.MetaContext;
import com.listeningframework.boot.codegen.context.SystemContext;
import com.listeningframework.boot.codegen.exception.BusinessException;
import com.listeningframework.boot.codegen.lang.FreemarkerUtils;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * Abstract Gnerators
 *
 * @author lixining
 * @version $Id: AbstractGenerator.java, v 0.1 2016年12月1日 下午7:17:57 lixining Exp $
 */
public abstract class AbstractCoder {
    /**
     * 包名
     */
    protected static final String CLASS_PACKAGE = "classPackage";
    /**
     * 模块功能描述
     */
    protected static final String MODEL_DESC = "modelDesc";
    /**
     * 模型名称前缀
     */
    protected static final String MODEL_PREFIX = "modelPrefix";
    /**
     * 模型后缀
     */
    protected static final String MODEL_SUFFIX = "modelSuffix";
    /**
     * 导入类库
     */
    protected static final String CLASS_IMPORTS = "classImports";
    /**
     * Metadata List
     */
    protected static final String CLASS_METADATA_LIST = "classMetaDataList";
    /**
     * 当前时间
     */
    protected static final String CURRENT_DATETIME = "currentDateTime";
    /**
     * Template Path
     */
    private static final String TEMPLATE = "/com/listeningframework/boot/generators/templates/";
    /**
     * MetaData Context
     */
    protected MetaContext context;

    public AbstractCoder(MetaContext context) {
        this.context = context;
    }

    /**
     * Generate Template Params
     *
     * @return Map
     */
    protected abstract Map<String, Object> getParams() throws BusinessException;

    /**
     * Generate Content
     *
     * @return String
     */
    protected abstract String getContent() throws BusinessException;

    /**
     * 源码存储路径
     */
    protected abstract String getSavePath() throws BusinessException;

    /**
     * 写源码文件到磁盘
     *
     * @throws BusinessException
     */
    public void writeSrc() throws BusinessException {
        if (!isEnable()) {
            return;
        }
        // 1. 如果是EntityCoder而且不读数据库表结构直接返回(Entity无需修改)
        if (this instanceof EntityCoder && !readDataBase()) {
            return;
        }
        boolean isMapper = (this instanceof MapperCoder);
        // 2. 如果是MapperCoder 而且持久化技术不是Mybatis,直接返回无需生成
        if (isMapper && !SystemContext.isORMMyBatis()) {
            return;
        }
        writeJavaAndResource(!isMapper);
    }

    /**
     * Write Utils Method
     *
     * @throws BusinessException
     */
    protected void writeJavaAndResource(boolean isJava) throws BusinessException {
        try {
            String path = getSavePath();
            String absPath = SystemContext.getSrcResourcesPath() + path;
            if (isJava) {
                absPath = SystemContext.getSrcJavaPath() + path;
            }
            File destFile = new File(absPath);
            if (!destFile.exists() || isOverride()) {
                FileUtils.write(destFile, getContent(), Charset.forName("utf-8"));
            }
        } catch (Exception e) {
            throw new BusinessException("write content fail", e);
        }
    }

    /**
     * 加载模板内容
     *
     * @param template 模板
     * @return String
     * @throws BusinessException
     */
    private String getTemplate(Templates template) throws BusinessException {
        InputStream input = null;
        try {
            String dbtype = SystemContext.getCodeGenConfig().orm().name();
            String prefixName = TEMPLATE + dbtype.toLowerCase() + "_" + template.getTemplate();
            input = AbstractCoder.class.getResourceAsStream(prefixName);
            if (input == null) {
                return "";
            }
            return IOUtils.toString(input, "utf-8");
        } catch (IOException e) {
            throw new BusinessException("模板加载失败", e);
        } finally {
            IOUtils.closeQuietly(input);
        }
    }

    /**
     * 渲染Freemarker模板
     *
     * @param template 模板
     * @return String
     * @throws BusinessException
     */
    protected String render(Templates template) throws BusinessException {
        try {
            Map<String, Object> params = getParams();
            String message = getTemplate(template);
            return FreemarkerUtils.getFreemarkerContent(message, params);
        } catch (BusinessException e) {
            throw e;
        } catch (Exception e) {
            throw new BusinessException("模板渲染失败", e);
        }
    }

    /**
     * 渲染模板
     *
     * @param template 模板
     * @return String
     * @throws BusinessException
     */
    protected String render(String template) throws BusinessException {
        try {
            Map<String, Object> params = getParams();
            return FreemarkerUtils.getFreemarkerContent(template, params);
        } catch (BusinessException e) {
            throw e;
        } catch (Exception e) {
            throw new BusinessException("模板渲染失败", e);
        }
    }

    /**
     * 是否要需要生成文件
     *
     * @return boolean
     */
    protected boolean isEnable() {
        return context.getCodeGenEntity().enabled();
    }

    /**
     * 是否要覆盖已有文件
     *
     * @return boolean
     */
    protected boolean isOverride() {
        return context.getCodeGenEntity().override();
    }

    /**
     * 是否读取DataBase
     *
     * @return boolean
     */
    protected boolean readDataBase() {
        return context.getCodeGenEntity().readDataBase();
    }

    /**
     * Maven项目Src(Java)目录
     *
     * @return String
     */
    protected String getSrcJavaPath() {
        return SystemContext.getSrcJavaPath();
    }

    /**
     * Maven项目Src(Resources)目录
     *
     * @return String
     */
    protected String getSrcResourcesPath() {
        return SystemContext.getSrcResourcesPath();
    }

    /**
     * 获取Common的模板参数
     *
     * @return Map<String, Object>
     */
    protected Map<String, Object> newHashMap() {
        Map<String, Object> paramMap = new HashMap<>();
        paramMap.put(MODEL_PREFIX, context.getModelPrefix());
        paramMap.put(MODEL_SUFFIX, context.getModelSuffix());
        paramMap.put(MODEL_DESC, context.getModelDesc());
        paramMap.put(CURRENT_DATETIME, new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
        return paramMap;
    }
}
